home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Aminet 45
/
Aminet 45 (2001)(GTI - Schatztruhe)[!][Oct 2001].iso
/
Aminet
/
gfx
/
x11
/
x3270_3_2_16.lha
/
amiga_src
/
select.c
< prev
next >
Wrap
C/C++ Source or Header
|
2009-02-27
|
28KB
|
1,232 lines
/*
* Copyright 1993, 1994, 1995, 1999, 2000 by Paul Mattes.
* Permission to use, copy, modify, and distribute this software and its
* documentation for any purpose and without fee is hereby granted,
* provided that the above copyright notice appear in all copies and that
* both that copyright notice and this permission notice appear in
* supporting documentation.
*/
/*
* Portions of this code were taken from xterm/button.c:
* Copyright 1987 by Digital Equipment Corporation, Maynard, Massachusetts.
*
* All Rights Reserved
*
* Permission to use, copy, modify, and distribute this software and its
* documentation for any purpose and without fee is hereby granted,
* provided that the above copyright notice appear in all copies and that
* both that copyright notice and this permission notice appear in
* supporting documentation, and that the name of Digital Equipment
* Corporation not be used in advertising or publicity pertaining to
* distribution of the software without specific, written prior permission.
*
*
* DIGITAL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
* ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL
* DIGITAL BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
* ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
* WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
* ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
* SOFTWARE.
*/
/*
* select.c
* This module handles selections.
*/
#include "globals.h"
#include <X11/Xatom.h>
#include <X11/Xmu/Atoms.h>
#include <X11/Xmu/StdSel.h>
#include "3270ds.h"
#include "appres.h"
#include "ctlr.h"
#include "screen.h"
#include "cg.h"
#include "resources.h"
#include "actionsc.h"
#include "ctlrc.h"
#include "kybdc.h"
#include "popupsc.h"
#include "screenc.h"
#include "selectc.h"
#include "tablesc.h"
#include "utilc.h"
#define Max(x, y) (((x) > (y))? (x): (y))
#define Min(x, y) (((x) < (y))? (x): (y))
/*
* Mouse side.
*/
/* A button click establishes the boundaries of the 'fixed' area. */
static int f_start = 0; /* 'fixed' area */
static int f_end = 0;
/* Mouse motion moves the boundaries of the 'varying' area. */
static int v_start = 0; /* 'varying' area */
static int v_end = 0;
static unsigned long down_time = 0;
static unsigned long down1_time = 0;
static Dimension down1_x, down1_y;
static unsigned long up_time = 0;
static int saw_motion = 0;
static int num_clicks = 0;
static void grab_sel(int start, int end, Boolean really, Time t);
#define NS 5
static Atom want_sel[NS];
static struct { /* owned selections */
Atom atom; /* atom */
char *buffer; /* buffer contents */
} own_sel[NS];
static Boolean cursor_moved = False;
static int saved_cursor_addr;
static void own_sels(Time t);
static int n_owned = -1;
static Boolean any_selected = False;
extern Widget *screen;
#define CLICK_INTERVAL 200
#define event_x(event) event->xbutton.x
#define event_y(event) event->xbutton.y
#define event_time(event) event->xbutton.time
#define BOUNDED_XY(event, x, y) { \
x = X_TO_COL(event_x(event)); \
if (x < 0) \
x = 0; \
if (x >= COLS) \
x = COLS - 1; \
if (flipped) \
x = (COLS - x) - 1; \
y = Y_TO_ROW(event_y(event) - *descent); \
if (y <= 0) \
y = 0; \
if (y >= ROWS) \
y = ROWS - 1; \
}
static int char_class[256] = {
/* nul soh stx etx eot enq ack bel bs ht nl vt np cr so si */
32, 1, 1, 1, 1, 1, 1, 1, 1, 32, 1, 1, 1, 1, 1, 1,
/* dle dc1 dc2 dc3 dc4 nak syn etb can em sub esc fs gs rs us */
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
/* sp ! " # $ % & ' ( ) * + , - . / */
32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47,
/* 0 1 2 3 4 5 6 7 8 9 : ; < = > ? */
48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 58, 59, 60, 61, 62, 63,
/* @ A B C D E F G H I J K L M N O */
64, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48,
/* P Q R S T U V W X Y Z [ \ ] ^ _ */
48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 91, 92, 93, 94, 48,
/* ` a b c d e f g h i j k l m n o */
96, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48,
/* p q r s t u v w x y z { | } ~ del */
48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48,123,124,125,126, 1,
/* ---,---,---,---,---,---,---,---,---,---,---,---,---,---,---,--- */
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
/* ---,---,---,---,---,---,---,---,---,---,---,---,---,---,---,--- */
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
/* nob exc cen ste cur yen bro sec dia cop ord gui not hyp reg mac */
32,161,162,163,164,165,166,167,168,169,170,171,172,173,174,175,
/* deg plu two thr acu mu par per ce one mas gui one one thr que */
176,177,178,179,180,181,182,183,184,185,186,178,188,189,190,191,
/* Agr Aac Aci Ati Adi Ari AE Cce Egr Eac Eci Edi Igr Iac Ici Idi */
48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48,
/* ETH Nti Ogr Oac Oci Oti Odi mul Oob Ugr Uac Uci Udi Yac THO ssh */
48, 48, 48, 48, 48, 48, 48,215, 48, 48, 48, 48, 48, 48, 48, 48,
/* agr aac aci ati adi ari ae cce egr eac eci edi igr iac ici idi */
48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48,
/* eth nti ogr oac oci oti odi div osl ugr uac uci udi yac tho ydi */
48, 48, 48, 48, 48, 48, 48,247, 48, 48, 48, 48, 48, 48, 48, 48
};
/* Parse a charClass string: [low-]high:value[,...] */
void
reclass(char *s)
{
int n;
int low, high, value;
int i;
char c;
n = -1;
low = -1;
high = -1;
for (;;) {
c = *s++;
if (isdigit(c)) {
if (n == -1)
n = 0;
n = (n * 10) + (c - '0');
if (n > 255)
goto fail;
} else if (c == '-') {
if (n == -1 || low != -1)
goto fail;
low = n;
n = -1;
} else if (c == ':') {
if (n == -1)
goto fail;
high = n;
n = -1;
} else if (c == ',' || c == '\0') {
if (n == -1)
goto fail;
value = n;
n = -1;
if (high == -1)
goto fail;
if (low == -1)
low = high;
if (high < low)
goto fail;
for (i = low; i <= high; i++)
char_class[i] = value;
low = -1;
high = -1;
if (c == '\0')
return;
} else
goto fail;
}
fail:
xs_warning("Error in %s string", ResCharClass);
}
static void
select_word(int baddr, Time t)
{
unsigned char fa = *get_field_attribute(baddr);
unsigned char ch;
int class;
/* Find the initial character class */
if (FA_IS_ZERO(fa))
ch = CG_space;
else
ch = screen_buf[baddr];
class = char_class[cg2asc[ch]];
/* Find the beginning */
for (f_start = baddr; f_start % COLS; f_start--) {
fa = *get_field_attribute(f_start);
if (FA_IS_ZERO(fa))
ch = CG_space;
else
ch = screen_buf[f_start];
if (char_class[cg2asc[ch]] != class) {
f_start++;
break;
}
}
/* Find the end */
for (f_end = baddr; (f_end+1) % COLS; f_end++) {
fa = *get_field_attribute(f_end);
if (FA_IS_ZERO(fa))
ch = CG_space;
else
ch = screen_buf[f_end];
if (char_class[cg2asc[ch]] != class) {
f_end--;
break;
}
}
v_start = f_start;
v_end = f_end;
grab_sel(f_start, f_end, True, t);
}
static void
select_line(int baddr, Time t)
{
f_start = baddr - (baddr % COLS);
f_end = f_start + COLS - 1;
v_start = f_start;
v_end = f_end;
grab_sel(f_start, f_end, True, t);
}
/*
* Start a new selection.
* Usually bound to <Btn1Down>.
*/
void
select_start_action(Widget w, XEvent *event, String *params,
Cardinal *num_params)
{
int x, y;
register int baddr;
action_debug(select_start_action, event, params, num_params);
if (event == NULL) {
popup_an_error("%s can only be used as a keymap action",
action_name(select_start_action));
return;
}
if (w != *screen)
return;
BOUNDED_XY(event, x, y);
baddr = ROWCOL_TO_BA(y, x);
f_start = f_end = v_start = v_end = baddr;
down1_time = down_time = event_time(event);
down1_x = event_x(event);
down1_y = event_y(event);
if (down_time - up_time > CLICK_INTERVAL) {
num_clicks = 0;
/* Commit any previous cursor move. */
cursor_moved = False;
}
if (num_clicks == 0)
unselect(0, ROWS*COLS);
}
/*
* Alternate form of select_start, which combines cursor motion with selection.
* Usually bound to <Btn1Down> in a user-specified keymap.
*/
void
move_select_action(Widget w, XEvent *event, String *params,
Cardinal *num_params)
{
int x, y;
register int baddr;
action_debug(move_select_action, event, params, num_params);
if (event == NULL) {
popup_an_error("%s can only be used as a keymap action",
action_name(move_select_action));
return;
}
if (w != *screen)
return;
BOUNDED_XY(event, x, y);
baddr = ROWCOL_TO_BA(y, x);
f_start = f_end = v_start = v_end = baddr;
down1_time = down_time = event_time(event);
down1_x = event_x(event);
down1_y = event_y(event);
if (down_time - up_time > CLICK_INTERVAL) {
num_clicks = 0;
/* Commit any previous cursor move. */
cursor_moved = False;
}
if (num_clicks == 0) {
if (any_selected) {
unselect(0, ROWS*COLS);
} else {
cursor_moved = True;
saved_cursor_addr = cursor_addr;
cursor_move(baddr);
}
}
}
/*
* Begin extending the current selection.
* Usually bound to <Btn3Down>.
*/
void
start_extend_action(Widget w, XEvent *event, String *params,
Cardinal *num_params)
{
int x, y;
int baddr;
Boolean continuous = (!ever_3270 && !toggled(RECTANGLE_SELECT));
action_debug(start_extend_action, event, params, num_params);
if (event == NULL) {
popup_an_error("%s can only be used as a keymap action",
action_name(start_extend_action));
return;
}
if (w != *screen)
return;
down1_time = 0L;
BOUNDED_XY(event, x, y);
baddr = ROWCOL_TO_BA(y, x);
if (continuous) {
/* Think linearly. */
if (baddr < f_start)
v_start = baddr;
else if (baddr > f_end)
v_end = baddr;
else if (baddr - f_start > f_end - baddr)
v_end = baddr;
else
v_start = baddr;
} else {
/* Think rectangularly. */
int nrow = baddr / COLS;
int ncol = baddr % COLS;
int vrow_ul = v_start / COLS;
int vrow_lr = v_end / COLS;
int vcol_ul = Min(v_start % COLS, v_end % COLS);
int vcol_lr = Max(v_start % COLS, v_end % COLS);
/* Set up the row. */
if (nrow <= vrow_ul)
vrow_ul = nrow;
else if (nrow >= vrow_lr)
vrow_lr = nrow;
else if (nrow - vrow_ul > vrow_lr - nrow)
vrow_lr = nrow;
else
vrow_ul = nrow;
/* Set up the column. */
if (ncol <= vcol_ul)
vcol_ul = ncol;
else if (ncol >= vcol_lr)
vcol_lr = ncol;
else if (ncol - vcol_ul > vcol_lr - ncol)
vcol_lr = ncol;
else
vcol_ul = ncol;
v_start = (vrow_ul * COLS) + vcol_ul;
v_end = (vrow_lr * COLS) + vcol_lr;
}
grab_sel(v_start, v_end, True, event_time(event));
saw_motion = 1;
num_clicks = 0;
}
/*
* Continuously extend the current selection.
* Usually bound to <Btn1Motion> and <Btn3Motion>.
*/
void
select_extend_action(Widget w, XEvent *event, String *params,
Cardinal *num_params)
{
int x, y;
int baddr;
action_debug(select_extend_action, event, params, num_params);
if (event == NULL) {
popup_an_error("%s can only be used as a keymap action",
action_name(select_extend_action));
return;
}
if (w != *screen)
return;
/* Ignore initial drag events if are too near. */
if (down1_time != 0L &&
abs((int) event_x(event) - (int) down1_x) < *char_width &&
abs((int) event_y(event) - (int) down1_y) < *char_height)
return;
else
down1_time = 0L;
/* If we moved the 3270 cursor on the first click, put it back. */
if (cursor_moved) {
cursor_move(saved_cursor_addr);
cursor_moved = False;
}
BOUNDED_XY(event, x, y);
baddr = ROWCOL_TO_BA(y, x);
/*
* If baddr falls outside if the v range, open up the v range. In
* addition, if we are extending one end of the v range, make sure the
* other end at least covers the f range.
*/
if (baddr <= v_start) {
v_start = baddr;
v_end = f_end;
}
if (baddr >= v_end) {
v_end = baddr;
v_start = f_start;
}
/*
* If baddr falls within the v range, narrow up the nearer end of the
* v range.
*/
if (baddr > v_start && baddr < v_end) {
if (baddr - v_start < v_end - baddr)
v_start = baddr;
else
v_end = baddr;
}
num_clicks = 0;
saw_motion = 1;
grab_sel(v_start, v_end, False, event_time(event));
}
/*
* End the selection.
* Usually bound to <BtnUp>.
*/
void
select_end_action(Widget w unused, XEvent *event, String *params,
Cardinal *num_params)
{
Cardinal i;
int x, y;
action_debug(select_end_action, event, params, num_params);
if (event == NULL) {
popup_an_error("%s can only be used as a keymap action",
action_name(select_end_action));
return;
}
if (w != *screen)
return;
if (n_owned == -1) {
for (i = 0; i < NS; i++)
own_sel[i].atom = None;
n_owned = 0;
}
for (i = 0; i < NS; i++)
if (i < *num_params)
want_sel[i] = XInternAtom(display, params[i], False);
else
want_sel[i] = None;
if (*num_params == 0)
want_sel[0] = XA_PRIMARY;
BOUNDED_XY(event, x, y);
up_time = event_time(event);
if (up_time - down_time > CLICK_INTERVAL)
num_clicks = 0;
if (++num_clicks > 3)
num_clicks = 1;
switch (num_clicks) {
case 1:
if (saw_motion) {
f_start = v_start;
f_end = v_end;
grab_sel(f_start, f_end, True, event_time(event));
}
break;
case 2:
/*
* If we moved the 3270 cursor on the first click, put it back.
*/
if (cursor_moved) {
cursor_move(saved_cursor_addr);
cursor_moved = False;
}
select_word(f_start, event_time(event));
break;
case 3:
select_line(f_start, event_time(event));
break;
}
saw_motion = 0;
}
/*
* Set the selection.
* Usually bound to the Copy key.
*/
void
set_select_action(Widget w unused, XEvent *event, String *params,
Cardinal *num_params)
{
Cardinal i;
action_debug(set_select_action, event, params, num_params);
if (!any_selected)
return;
if (n_owned == -1) {
for (i = 0; i < NS; i++)
own_sel[i].atom = None;
n_owned = 0;
}
for (i = 0; i < NS; i++)
if (i < *num_params)
want_sel[i] = XInternAtom(display, params[i], False);
else
want_sel[i] = None;
if (*num_params == 0)
want_sel[0] = XA_PRIMARY;
own_sels(event_time(event));
}
/*
* Translate the mouse position to a buffer address.
*/
int
mouse_baddr(Widget w, XEvent *event)
{
int x, y;
if (w != *screen)
return 0;
BOUNDED_XY(event, x, y);
return ROWCOL_TO_BA(y, x);
}
/*
* Cut action.
* For now, merely erases all unprotected characters currently selected.
* In future, it may interact more with selections.
*/
#define ULS sizeof(unsigned long)
#define ULBS (ULS * 8)
void
Cut_action(Widget w unused, XEvent *event, String *params, Cardinal *num_params)
{
register int baddr;
unsigned char fa = *get_field_attribute(0);
unsigned long *target;
register unsigned char repl;
action_debug(Cut_action, event, params, num_params);
target = (unsigned long *)XtCalloc(ULS, ((ROWS*COLS)+(ULBS-1))/ULBS);
/* Identify the positions to empty. */
for (baddr = 0; baddr < ROWS*COLS; baddr++) {
unsigned char c = screen_buf[baddr];
if (IS_FA(c))
fa = c;
else if ((IN_ANSI || !FA_IS_PROTECTED(fa)) && SELECTED(baddr))
target[baddr/ULBS] |= 1 << (baddr%ULBS);
}
/* Erase them. */
if (IN_3270)
repl = CG_null;
else
repl = CG_space;
for (baddr = 0; baddr < ROWS*COLS; baddr++)
if (target[baddr/ULBS] & (1 << (baddr%ULBS)))
ctlr_add(baddr, repl, 0);
XtFree((XtPointer)target);
}
/*
* KybdSelect action. Extends the selection area in the indicated direction.
*/
void
KybdSelect_action(Widget w unused, XEvent *event, String *params,
Cardinal *num_params)
{
enum { UP, DOWN, LEFT, RIGHT } direction;
int x_start, x_end;
int i;
action_debug(KybdSelect_action, event, params, num_params);
if (event == NULL) {
popup_an_error("%s can only be used as a keymap action",
action_name(select_start_action));
return;
}
if (w != *screen)
return;
if (*num_params < 1) {
popup_an_error("%s requires one argument",
action_name(KybdSelect_action));
return;
}
if (!strcasecmp(params[0], "Up")) {
direction = UP;
} else if (!strcasecmp(params[0], "Down")) {
direction = DOWN;
} else if (!strcasecmp(params[0], "Left")) {
direction = LEFT;
} else if (!strcasecmp(params[0], "Right")) {
direction = RIGHT;
} else {
popup_an_error("%s first argument must be Up, Down, Left, or "
"Right", action_name(KybdSelect_action));
return;
}
if (!any_selected)
x_start = x_end = cursor_addr;
else {
if (f_start < f_end) {
x_start = f_start;
x_end = f_end;
} else {
x_start = f_end;
x_end = f_start;
}
}
switch (direction) {
case UP:
if (!(x_start / COLS))
return;
x_start -= COLS;
break;
case DOWN:
if ((x_end / COLS) == ROWS - 1)
return;
x_end += COLS;
break;
case LEFT:
if (!(x_start % COLS))
return;
x_start--;
break;
case RIGHT:
if ((x_end % COLS) == COLS - 1)
return;
x_end++;
break;
}
/* Figure out the atoms they want. */
if (n_owned == -1) {
for (i = 0; i < NS; i++)
own_sel[i].atom = None;
n_owned = 0;
}
for (i = 1; i < NS; i++)
if (i < *num_params)
want_sel[i] = XInternAtom(display, params[i], False);
else
want_sel[i] = None;
if (*num_params == 1)
want_sel[0] = XA_PRIMARY;
/* Grab the selection. */
f_start = v_start = x_start;
f_end = v_end = x_end;
grab_sel(f_start, f_end, True, event_time(event));
}
/*
* unselect action. Removes a selection.
*/
void
Unselect_action(Widget w unused, XEvent *event, String *params,
Cardinal *num_params)
{
action_debug(Unselect_action, event, params, num_params);
/* It's just cosmetic. */
unselect(0, ROWS*COLS);
}
/*
* Screen side.
*/
static char *select_buf = CN;
static char *sb_ptr = CN;
static int sb_size = 0;
#define SB_CHUNK 1024
static Time sel_time;
static void
init_select_buf(void)
{
if (select_buf == CN)
select_buf = XtMalloc(sb_size = SB_CHUNK);
sb_ptr = select_buf;
}
static void
store_sel(char c)
{
if (sb_ptr - select_buf >= sb_size) {
sb_size += SB_CHUNK;
select_buf = XtRealloc(select_buf, sb_size);
sb_ptr = select_buf + sb_size - SB_CHUNK;
}
*(sb_ptr++) = c;
}
static Boolean
convert_sel(Widget w, Atom *selection, Atom *target, Atom *type,
XtPointer *value, unsigned long *length, int *format)
{
int i;
/* Find the right selection. */
for (i = 0; i < NS; i++)
if (own_sel[i].atom == *selection)
break;
if (i >= NS) /* not my selection */
return False;
if (*target == XA_TARGETS(display)) {
Atom* targetP;
Atom* std_targets;
unsigned long std_length;
XmuConvertStandardSelection(w, sel_time, selection,
target, type, (caddr_t*) &std_targets, &std_length, format);
*length = std_length + 5;
*value = (XtPointer) XtMalloc(sizeof(Atom) * (*length));
targetP = *(Atom**)value;
*targetP++ = XA_STRING;
*targetP++ = XA_TEXT(display);
*targetP++ = XA_COMPOUND_TEXT(display);
*targetP++ = XA_LENGTH(display);
*targetP++ = XA_LIST_LENGTH(display);
(void) memmove(targetP, std_targets,
(int) (sizeof(Atom) * std_length));
XtFree((char *) std_targets);
*type = XA_ATOM;
*format = 32;
return True;
}
if (*target == XA_STRING ||
*target == XA_TEXT(display) ||
*target == XA_COMPOUND_TEXT(display)) {
if (*target == XA_COMPOUND_TEXT(display))
*type = *target;
else
*type = XA_STRING;
*length = strlen(own_sel[i].buffer);
*value = XtMalloc(*length);
(void) memmove(*value, own_sel[i].buffer, (int) *length);
*format = 8;
return True;
}
if (*target == XA_LIST_LENGTH(display)) {
*value = XtMalloc(4);
if (sizeof(long) == 4)
*(long *)*value = 1;
else {
long temp = 1;
(void) memmove(*value,
((char*) &temp) + sizeof(long) - 4,
4);
}
*type = XA_INTEGER;
*length = 1;
*format = 32;
return True;
}
if (*target == XA_LENGTH(display)) {
*value = XtMalloc(4);
if (sizeof(long) == 4)
*(long*)*value = strlen(own_sel[i].buffer);
else {
long temp = strlen(own_sel[i].buffer);
(void) memmove(*value,
((char *) &temp) + sizeof(long) - 4,
4);
}
*type = XA_INTEGER;
*length = 1;
*format = 32;
return True;
}
if (XmuConvertStandardSelection(w, sel_time, selection,
target, type, (caddr_t *)value, length, format))
return True;
/* else */
#if 0
printf("Unknown conversion request: %s to %s\n",
XGetAtomName(display, *selection),
XGetAtomName(display, *target));
#endif
return False;
}
static void
lose_sel(Widget w unused, Atom *selection)
{
int i;
for (i = 0; i < NS; i++)
if (own_sel[i].atom != None && own_sel[i].atom == *selection) {
own_sel[i].atom = None;
XtFree(own_sel[i].buffer);
own_sel[i].buffer = CN;
n_owned--;
break;
}
if (!n_owned)
unselect(0, ROWS*COLS);
}
/*
* Somewhat convoluted logic to return an ASCII character for a given screen
* position.
*
* The character has to be found indirectly from screen_buf and the field
* attirbutes, so that zero-intensity fields become blanks.
*/
static Boolean osc_valid = False;
static void
osc_start(void)
{
osc_valid = False;
}
/*
* Return a 'selection' version of a given character on the screen.
* Returns a printable ASCII character, or 0 if the character is a NULL and
* shouldn't be included in the selection.
* Also returns in 'ge' whether or not an ESC (0x1b) should be included
* in front of the character.
*/
static unsigned char
onscreen_char(int baddr, Boolean *ge)
{
static int osc_baddr;
static unsigned char fa;
/* If we aren't moving forward, all bets are off. */
if (osc_valid && baddr < osc_baddr)
osc_valid = False;
if (osc_valid) {
/*
* Search for a new field attribute between the address we
* want and the last address we searched. If we found a new
* field attribute, save the address for next time.
*/
(void) get_bounded_field_attribute(baddr, osc_baddr, &fa);
osc_baddr = baddr;
} else {
/*
* Find the attribute the old way.
*/
fa = *get_field_attribute(baddr);
osc_baddr = baddr;
osc_valid = True;
}
/* Assume it isn't a graphic escape. */
*ge = False;
/* If it isn't visible, then make it a blank. */
if (FA_IS_ZERO(fa))
return ' ';
switch (ea_buf[baddr].cs) {
case 0:
default:
switch (screen_buf[baddr]) {
case CG_null:
return 0;
case CG_fm:
*ge = True;
return ';';
case CG_dup:
*ge = True;
return '*';
default:
return cg2asc[screen_buf[baddr]];
}
case CS_GE:
switch (screen_buf[baddr]) {
case CG_null:
return 0;
case CG_Yacute:
return '[';
case CG_diaeresis:
return ']';
default:
*ge = True;
return cg2asc[screen_buf[baddr]];
}
case 2:
/* vt100 line-drawing character */
return screen_buf[baddr] + 0x5f;
}
}
/*
* Attempt to own the selections in want_sel[].
*/
static void
own_sels(Time t)
{
register int i, j;
/*
* Try to grab any new selections we may want.
*/
for (i = 0; i < NS; i++) {
Boolean already_own = False;
if (want_sel[i] == None)
continue;
/* Check if we already own it. */
for (j = 0; j < NS; j++)
if (own_sel[j].atom == want_sel[i]) {
already_own = True;
break;
}
/* Find the slot for it. */
if (!already_own) {
for (j = 0; j < NS; j++)
if (own_sel[j].atom == None)
break;
if (j >= NS)
continue;
}
if (XtOwnSelection(*screen, want_sel[i], t, convert_sel,
lose_sel, NULL)) {
if (!already_own) {
n_owned++;
own_sel[j].atom = want_sel[i];
}
if (own_sel[j].buffer != CN)
XtFree(own_sel[j].buffer);
own_sel[j].buffer = XtMalloc(strlen(select_buf) + 1);
(void) memmove(own_sel[j].buffer, select_buf,
strlen(select_buf) + 1);
} else {
XtWarning("Could not get selection");
if (own_sel[j].atom != None) {
XtFree(own_sel[j].buffer);
own_sel[j].buffer = CN;
own_sel[j].atom = None;
n_owned--;
}
}
}
if (!n_owned)
unselect(0, ROWS*COLS);
sel_time = t;
}
/*
* Copy the selected area on the screen into a buffer and attempt to
* own the selections in want_sel[].
*/
static void
grab_sel(int start, int end, Boolean really, Time t)
{
register int i;
int start_row, end_row;
unsigned char osc;
Boolean ge;
unselect(0, ROWS*COLS);
if (start > end) {
int exch = end;
end = start;
start = exch;
}
start_row = start / COLS;
end_row = end / COLS;
init_select_buf(); /* prime the store_sel() routine */
osc_start(); /* prime the onscreen_char() routine */
if (!ever_3270 && !toggled(RECTANGLE_SELECT)) {
/* Continuous selections */
for (i = start; i <= end; i++) {
SET_SELECT(i);
if (really) {
if (i != start && !(i % COLS))
store_sel('\n');
osc = onscreen_char(i, &ge);
if (osc) {
if (ge)
store_sel('\033');
store_sel((char)osc);
}
}
}
/* Check for newline extension */
if ((end % COLS) != (COLS - 1)) {
Boolean all_blank = True;
for (i = end; i < end + (COLS - (end % COLS)); i++) {
osc = onscreen_char(i, &ge);
if (osc && osc != ' ') {
all_blank = False;
break;
}
}
if (all_blank) {
for (i = end; i < end + (COLS - (end % COLS)); i++) {
SET_SELECT(i);
}
store_sel('\n');
}
}
} else {
/* Rectangular selections */
if (start_row == end_row) {
for (i = start; i <= end; i++) {
SET_SELECT(i);
if (really) {
osc = onscreen_char(i, &ge);
if (osc) {
if (ge)
store_sel('\033');
store_sel((char)osc);
}
}
}
if (really && (end % COLS == COLS - 1))
store_sel('\n');
} else {
int row, col;
int start_col = start % COLS;
int end_col = end % COLS;
if (start_col > end_col) {
int exch = end_col;
end_col = start_col;
start_col = exch;
}
for (row = start_row; row <= end_row; row++) {
for (col = start_col; col <= end_col; col++) {
SET_SELECT(row*COLS + col);
if (really) {
osc = onscreen_char(row*COLS + col, &ge);
if (osc) {
if (ge)
store_sel('\033');
store_sel((char)osc);
}
}
}
if (really)
store_sel('\n');
}
}
}
/* Terminate the result. */
if (really)
store_sel('\0');
any_selected = True;
ctlr_changed(0, ROWS*COLS);
if (really)
own_sels(t);
}
/*
* Check if any character in a given region is selected.
*/
Boolean
area_is_selected(int baddr, int len)
{
register int i;
for (i = 0; i < len; i++)
if (SELECTED(baddr+i))
return True;
return False;
}
/*
* Unhighlight the region of selected text -- but don't give up the selection.
*/
void
unselect(int baddr unused, int len unused)
{
if (any_selected) {
(void) memset((char *) selected, 0, (ROWS*COLS + 7) / 8);
ctlr_changed(0, ROWS*COLS);
any_selected = False;
}
}
/* Selection insertion. */
#define NP 5
static Atom paste_atom[NP];
static int n_pasting = 0;
static int pix = 0;
static Time paste_time;
static void
paste_callback(Widget w, XtPointer client_data unused, Atom *selection unused,
Atom *type unused, XtPointer value, unsigned long *length,
int *format unused)
{
char *s;
unsigned long len;
if ((value == NULL) || (*length == 0)) {
XtFree(value);
/* Try the next one. */
if (n_pasting > pix)
XtGetSelectionValue(w, paste_atom[pix++], XA_STRING,
paste_callback, NULL, paste_time);
return;
}
s = (char *)value;
len = *length;
(void) emulate_input(s, (int) len, True);
n_pasting = 0;
XtFree(value);
}
void
insert_selection_action(Widget w, XEvent *event, String *params,
Cardinal *num_params)
{
Cardinal i;
Atom a;
XButtonEvent *be = (XButtonEvent *)event;
action_debug(insert_selection_action, event, params, num_params);
n_pasting = 0;
for (i = 0; i < *num_params; i++) {
a = XInternAtom(display, params[i], True);
if (a == None) {
popup_an_error("%s: No atom for selection",
action_name(insert_selection_action));
continue;
}
if (n_pasting < NP)
paste_atom[n_pasting++] = a;
}
pix = 0;
if (n_pasting > pix) {
paste_time = be->time;
XtGetSelectionValue(w, paste_atom[pix++], XA_STRING,
paste_callback, NULL, paste_time);
}
}